#pragma once
#include"meta_stream.hpp"
#include"offset_pointer.hpp"
#include"auto_aggregate_deduce.hpp"
#include"Algo.hpp"

using namespace meta_ios;
using namespace offset_pointer;

template<class T>
concept can_be_empty_constructed = requires(T) {
    T{};
};

template<class ...Tys>
class aligned_field
{
public:
    using member_list_istream = meta_istream_list<Tys...>;
    static constexpr std::size_t this_align = decl_align<Tys...>;


    template<std::size_t I>
    using select_member =
        meta_fold<
        transfer<I + 1, meta_getch, member_list_istream>,
        protocols::stream_to_t>;
    using this_for_stream = meta_for<
        align_offset_calc_ostream<this_align>,
        member_list_istream>;
    static constexpr std::size_t size = field_size<Tys...>;

    template<std::size_t I>
    using PTR_T = meta_fold<
        transfer<I + 1, align_offset_calc_ostream<this_align>, member_list_istream>, protocols::stream_to_t>;
};

template<class ...Tys>
class aligned_field_ptr;

template<class ...Tys>
class static_aligned_field : public aligned_field<Tys...> {
    using aligned_field<Tys...>::select_member;
    using aligned_field<Tys...>::this_for_stream;
    using aligned_field<Tys...>::this_align;
    using aligned_field<Tys...>::member_list_istream;
    using aligned_field<Tys...>::PTR_T;
private:
    alignas(aligned_field<Tys...>::this_align) std::byte data[field_size<Tys...>]{};
public:

    static_aligned_field() {
        this_for_stream::for_each(
            protocol_call<protocols::stream_to_t>([this]<class PTR_T>(PTR_T) {
            if constexpr (can_be_empty_constructed<typename PTR_T::type>)
                new(this->data + PTR_T::ptr::offset_v) typename PTR_T::type{};
            else {
                static_assert(literal_types::error<typename PTR_T::type>
                    ::template message<"error: must provide a value to initialize"_exp_str>::value);
            }
        })
        );
    }
    template<class First, class ...Vals>
    static_aligned_field(First const& first, Vals const& ...vals) {
        this_for_stream::for_each_forward(
            protocol_call<protocols::stream_to_t>([this]<class PTR_T>(PTR_T, auto const& val) {
            new(this->data + PTR_T::ptr::offset_v) typename PTR_T::type{ val };
        }),
            first, vals...
        );
    }
    template<class S>
    S& cast() {
        static_assert(alignof(S) == this_align);
        static_assert(std::is_same_v<decltype(deduct_constructor(S{})), exp_list<Tys... >> );
        return *reinterpret_cast<std::add_pointer_t<S>>(data);
    }
    template<std::size_t I>
    select_member<I>& read() {
        return *(reinterpret_cast<meta_fold<select_member<I>, std::add_pointer_t>>(data + PTR_T<I>::ptr::offset_v));
    }

    aligned_field_ptr<Tys...> as_ptr() {
        return { data };
    }

};

template<class First, class ...Rest>
static_aligned_field(First const&, Rest const&...) -> static_aligned_field<First, Rest...>;



template<class S>
using field_of = typename decltype(deduct_constructor(std::declval<S>()))::template to<static_aligned_field>;


template<class ...Tys>
class aligned_field_ptr : public aligned_field<Tys...>
{
    using aligned_field<Tys...>::select_member;
    using aligned_field<Tys...>::this_for_stream;
    using aligned_field<Tys...>::this_align;
    using aligned_field<Tys...>::member_list_istream;
    using aligned_field<Tys...>::PTR_T;
    std::byte* data;
public:
    aligned_field_ptr(auto* ptr) :data(reinterpret_cast<std::byte*>(ptr)) {}

    template<std::size_t I>
    select_member<I>& read() {
        return *(reinterpret_cast<meta_fold<select_member<I>, std::add_pointer_t>>(data + PTR_T<I>::ptr::offset_v));
    }

    template<std::size_t I>
    void initialize(const select_member<I>& val) {
        new(data + PTR_T<I>::ptr::offset_v) select_member<I>{val};
    }

   
};

template<class S>
using field_ptr_of = typename decltype(deduct_constructor(std::declval<S>()))::template to<aligned_field_ptr>;